#include "../config.h"

#include <string.h>
#include <gtk/gtk.h>
#include <gtk/gtkprivate.h>

#include "gtkcellrendererrelay.h"

enum {
  PROP_0,

  PROP_RELAY,
  PROP_RELAY_GDK,
  PROP_RELAY_SET,

  PROP_NABLA,

  PROP_RELAY_HEIGHT,
  PROP_RELAY_HEIGHT_SET
};

static void gtk_cell_renderer_relay_get_size(GtkCellRenderer *cell,
                                             GtkWidget       *widget,
                                             GdkRectangle    *cell_area,
                                             gint            *x_offset,
                                             gint            *y_offset,
                                             gint            *width,
                                             gint            *height) {
  GtkCellRendererRelay    *cell_relay   = GTK_CELL_RENDERER_RELAY(cell);
  GtkCellRendererTextClass *parent_class = g_type_class_peek(GTK_TYPE_CELL_RENDERER_TEXT);
  GtkCellRendererClass     *cell_class   = GTK_CELL_RENDERER_CLASS(parent_class);
  GdkRectangle              cell_area_text;
  gint                      save_wrap_width = -1;

  g_object_get(cell_relay, "wrap-width", &save_wrap_width, NULL);

  gsize size = 0;
  if (cell_relay->relay_height_set) {
    size = MAX(cell_relay->relay_height + 2, 8);
  } else {
    if (save_wrap_width > 0) g_object_set(cell_relay, "wrap-width", -1, NULL);
    gint x = 0, y = 0, w = 0, h = 0;
    cell_class->get_size(cell, widget, NULL, &x, &y, &w, &h);
    size = h - 2;
  }
  if (save_wrap_width > 0) g_object_set(cell_relay, "wrap-width", save_wrap_width - size - 2, NULL);
  if (cell_area) {
    cell_area_text.x      = cell_area->x + size + 2;
    cell_area_text.y      = cell_area->y;
    cell_area_text.width  = cell_area->width - size - 2;
    cell_area_text.height = cell_area->height;
  }
  cell_class->get_size(cell, widget, cell_area ? &cell_area_text : NULL, x_offset, y_offset, width, height);
  if (save_wrap_width > 0) g_object_set(cell_relay, "wrap-width", save_wrap_width, NULL);
  *width += size + 2;
}

static void gtk_cell_renderer_relay_render(GtkCellRenderer     *cell,
                                           GdkWindow           *window,
                                           GtkWidget           *widget,
                                           GdkRectangle        *background_area,
                                           GdkRectangle        *cell_area,
                                           GdkRectangle        *expose_area,
                                           GtkCellRendererState flags) {
  GtkCellRendererRelay    *cell_relay   = GTK_CELL_RENDERER_RELAY(cell);
  GtkCellRendererTextClass *parent_class = g_type_class_peek(GTK_TYPE_CELL_RENDERER_TEXT);
  GtkCellRendererClass     *cell_class   = GTK_CELL_RENDERER_CLASS(parent_class);
  GdkRectangle              cell_area_text;
  gint                      save_wrap_width = -1;
  guint                     xpad = 0, ypad = 0;
  gsize size = cell_relay->relay_height_set ? cell_relay->relay_height : (unsigned)cell_area->height - 2;
  memcpy(&cell_area_text, cell_area, sizeof(cell_area_text));
  cell_area_text.x += size + 4;
  cell_area_text.width -= size + 4;
  g_object_get(cell_relay,
               "wrap-width", &save_wrap_width,
               "xpad",       &xpad,
               "ypad",       &ypad,
               NULL);
  if (save_wrap_width > 0) g_object_set(cell_relay, "wrap-width", save_wrap_width - size - 2, NULL);
  cell_class->render(cell, window, widget, background_area, &cell_area_text, expose_area, flags);
  if (save_wrap_width > 0) g_object_set(cell_relay, "wrap-width", save_wrap_width, NULL);

  GdkGC *gc = gdk_gc_new(GDK_DRAWABLE(window));
  gsize x = cell_area->x + 1 + xpad, y = cell_area->y + 1 + ypad;
  GdkColor color;
  if (cell_relay->nabla) {
    color.red   = 0x8000;
    color.green = 0x8000;
    color.blue  = 0x8000;
    gdk_gc_set_rgb_fg_color(gc, &color);
    gdk_draw_rectangle(GDK_DRAWABLE(window), gc, TRUE, x, y, size, size);

    GdkPoint pts[5] = {
      {x, y},
      {x + size - 1, y},
      {x + size / 2, y + size - 1},
      {x, y},
      {x, y}
    };
    gsize ptl = 4;
    if ((size & 1) == 0) {
      pts[3].x = pts[2].x - 1;
      pts[3].y = pts[2].y;
      ptl = 5;
    }
    if (cell_relay->relay_set) {
      color.red   = cell_relay->relay.red;
      color.green = cell_relay->relay.green;
      color.blue  = cell_relay->relay.blue;
      gdk_gc_set_rgb_fg_color(gc, &color);
      gdk_draw_polygon(GDK_DRAWABLE(window), gc, TRUE, pts, ptl);
    }
    color.red   = 0;
    color.green = 0;
    color.blue  = 0;
    gdk_gc_set_rgb_fg_color(gc, &color);
    gdk_draw_polygon(GDK_DRAWABLE(window), gc, FALSE, pts, ptl);
  } else {
    if (cell_relay->relay_set) {
      color.red   = cell_relay->relay.red;
      color.green = cell_relay->relay.green;
      color.blue  = cell_relay->relay.blue;
      gdk_gc_set_rgb_fg_color(gc, &color);
      gdk_draw_rectangle(GDK_DRAWABLE(window), gc, TRUE, x, y, size, size);
    }
    color.red   = 0;
    color.green = 0;
    color.blue  = 0;
    gdk_gc_set_rgb_fg_color(gc, &color);
    gdk_draw_rectangle(GDK_DRAWABLE(window), gc, FALSE, x, y, size - 1, size - 1);
  }
}

static void gtk_cell_renderer_relay_get_property(GObject    *object,
                                                 guint       param_id,
                                                 GValue     *value,
                                                 GParamSpec *pspec) {
  GtkCellRendererRelay *cell_relay = GTK_CELL_RENDERER_RELAY(object);
  switch(param_id) {
  case PROP_RELAY_GDK:
    {
      GdkColor color;
      color.red   = cell_relay->relay.red;
      color.green = cell_relay->relay.green;
      color.blue  = cell_relay->relay.blue;
      g_value_set_boxed(value, &color);
    }
    break;
  case PROP_RELAY_SET:
    g_value_set_boolean(value, cell_relay->relay_set);
    break;
  case PROP_NABLA:
    g_value_set_boolean(value, cell_relay->nabla);
    break;
  case PROP_RELAY_HEIGHT:
    g_value_set_uint(value, cell_relay->relay_height);
    break;
  case PROP_RELAY_HEIGHT_SET:
    g_value_set_boolean(value, cell_relay->relay_height_set);
    break;
  default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
    break;
  }
}

static void gtk_cell_renderer_relay_set_property(GObject      *object,
                                                 guint         param_id,
                                                 const GValue *value,
                                                 GParamSpec   *pspec) {
  GtkCellRendererRelay *cell_relay = GTK_CELL_RENDERER_RELAY(object);
  switch(param_id) {
  case PROP_RELAY:
    {
      GdkColor color;
      if (!g_value_get_string(value)) {
        gtk_cell_renderer_relay_set_relay_gdk(cell_relay, NULL);
      } else if (gdk_color_parse(g_value_get_string(value), &color)) {
        gtk_cell_renderer_relay_set_relay_gdk(cell_relay, &color);
      } else {
        g_warning("Don't know color `%s'", g_value_get_string(value));
      }
    }
    break;
  case PROP_RELAY_GDK:
    gtk_cell_renderer_relay_set_relay_gdk(cell_relay, g_value_get_boxed(value));
    break;
  case PROP_RELAY_SET:
    gtk_cell_renderer_relay_set_relay_set(cell_relay, g_value_get_boolean(value));
    break;
  case PROP_NABLA:
    gtk_cell_renderer_relay_set_nabla(cell_relay, g_value_get_boolean(value));
    break;
  case PROP_RELAY_HEIGHT:
    gtk_cell_renderer_relay_set_relay_height(cell_relay, g_value_get_uint(value));
    break;
  case PROP_RELAY_HEIGHT_SET:
    gtk_cell_renderer_relay_set_relay_height_set(cell_relay, g_value_get_boolean(value));
    break;
  default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
    break;
  }
}

static void gtk_cell_renderer_relay_init(GtkCellRendererRelay *cellrelay) {
  cellrelay->relay.red   = 0xffff;
  cellrelay->relay.green = 0xffff;
  cellrelay->relay.blue  = 0xffff;
  cellrelay->relay_set   = TRUE;
  cellrelay->nabla       = FALSE;
}
static void gtk_cell_renderer_relay_class_init(GtkCellRendererRelayClass *class) {
  GObjectClass *object_class = G_OBJECT_CLASS(class);
  GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS(class);

  object_class->get_property = gtk_cell_renderer_relay_get_property;
  object_class->set_property = gtk_cell_renderer_relay_set_property;

  cell_class->get_size = gtk_cell_renderer_relay_get_size;
  cell_class->render   = gtk_cell_renderer_relay_render;

  g_object_class_install_property(object_class,
                                  PROP_RELAY,
                                  g_param_spec_string("relay",
                                                      "Relay color name",
                                                      "Relay color as a string",
                                                      NULL,
                                                      GTK_PARAM_WRITABLE));
  g_object_class_install_property(object_class,
                                  PROP_RELAY_GDK,
                                  g_param_spec_boxed("relay-gdk",
                                                     "Relay color",
                                                     "Relay color as a GdkColor",
                                                     GDK_TYPE_COLOR,
                                                     GTK_PARAM_READWRITE));

#define ADD_SET_PROP(propname, propval, nick, blurb) g_object_class_install_property (object_class, propval, g_param_spec_boolean (propname, nick, blurb, FALSE, GTK_PARAM_READWRITE))

  ADD_SET_PROP("relay-set", PROP_RELAY_SET,
               "Relay set",
               "Whether this tag affects the relay color");

  ADD_SET_PROP("nabla", PROP_NABLA, "Nabla", NULL);

  g_object_class_install_property(object_class,
                                  PROP_RELAY_HEIGHT,
                                  g_param_spec_uint("relay-height",
                                                    "Relay height",
                                                    "Relay height",
                                                    4,
                                                    256,
                                                    16,
                                                    GTK_PARAM_READWRITE));
  ADD_SET_PROP("relay-height-set", PROP_RELAY_HEIGHT_SET,
               "Relay height set",
               "Whether this tag affects the relay height");
}
GType gtk_cell_renderer_relay_get_type() {
  static GType r_type = 0;
  if (!r_type) {
    static const GTypeInfo r_info = {
      sizeof(GtkCellRendererRelayClass),
      NULL, /* base_init */
      NULL, /* base_finalize */
      (GClassInitFunc)gtk_cell_renderer_relay_class_init,
      NULL, /* class_finalize */
      NULL, /* class_data */
      sizeof(GtkCellRendererRelay),
      0,    /* n_preallocs */
      (GInstanceInitFunc)gtk_cell_renderer_relay_init,
      NULL  /* value_table */
    };
    r_type = g_type_register_static(GTK_TYPE_CELL_RENDERER_TEXT,
                                    "GtkCellRendererRelay",
                                    &r_info,
                                    (GTypeFlags)0);
  }
  return r_type;
}
GtkCellRenderer *gtk_cell_renderer_relay_new() {
  return g_object_new(GTK_TYPE_CELL_RENDERER_RELAY, NULL);
}

void gtk_cell_renderer_relay_set_relay_gdk(GtkCellRendererRelay *cell_relay,
                                           GdkColor             *relay) {
  if (relay) {
    cell_relay->relay.red   = relay->red;
    cell_relay->relay.green = relay->green;
    cell_relay->relay.blue  = relay->blue;
    g_object_notify(G_OBJECT(cell_relay), "relay-gdk");
    gtk_cell_renderer_relay_set_relay_set(cell_relay, TRUE);
  } else {
    gtk_cell_renderer_relay_set_relay_set(cell_relay, FALSE);
  }
}
void gtk_cell_renderer_relay_set_relay_set(GtkCellRendererRelay *cell_relay,
                                           gboolean              relay_set) {
  if (relay_set) {
    if (!cell_relay->relay_set) {
      cell_relay->relay_set = relay_set;
      g_object_notify(G_OBJECT(cell_relay), "relay-set");
    }
  } else {
    if (cell_relay->relay_set) {
      cell_relay->relay_set = relay_set;
      g_object_notify(G_OBJECT(cell_relay), "relay-set");
    }
  }
}
gboolean gtk_cell_renderer_relay_get_relay_set(GtkCellRendererRelay *cell_relay) {
  return cell_relay->relay_set;
}

void gtk_cell_renderer_relay_set_nabla(GtkCellRendererRelay *cell_relay,
                                       gboolean              nabla) {
  if (nabla) {
    if (!cell_relay->nabla) {
      cell_relay->nabla = nabla;
      g_object_notify(G_OBJECT(cell_relay), "nabla");
    }
  } else {
    if (cell_relay->nabla) {
      cell_relay->nabla = nabla;
      g_object_notify(G_OBJECT(cell_relay), "nabla");
    }
  }
}
gboolean gtk_cell_renderer_relay_get_nabla(GtkCellRendererRelay *cell_relay) {
  return cell_relay->nabla;
}

void gtk_cell_renderer_relay_set_relay_height(GtkCellRendererRelay *cell_relay,
                                              guint                 relay_height) {
  if (relay_height != cell_relay->relay_height) {
    cell_relay->relay_height = relay_height;
    g_object_notify(G_OBJECT(cell_relay), "relay-height");
  }
  gtk_cell_renderer_relay_set_relay_height_set(cell_relay, TRUE);
}
guint gtk_cell_renderer_relay_get_relay_height(GtkCellRendererRelay *cell_relay) {
  return cell_relay->relay_height;
}
void gtk_cell_renderer_relay_set_relay_height_set(GtkCellRendererRelay *cell_relay,
                                                  gboolean              relay_height_set) {
  if (relay_height_set) {
    if (!cell_relay->relay_height_set) {
      cell_relay->relay_height_set = relay_height_set;
      g_object_notify(G_OBJECT(cell_relay), "relay-height-set");
    }
  } else {
    if (cell_relay->relay_height_set) {
      cell_relay->relay_height_set = relay_height_set;
      g_object_notify(G_OBJECT(cell_relay), "relay-height-set");
    }
  }
}
gboolean gtk_cell_renderer_relay_get_relay_height_set(GtkCellRendererRelay *cell_relay) {
  return cell_relay->relay_height_set;
}
